package com.yiban.demo.ShuoShuo.Service.ServiceImp;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yiban.demo.ShuoShuo.DAO.ShuoShuoCommentMapper;
import com.yiban.demo.ShuoShuo.DAO.ShuoShuoMapper;
import com.yiban.demo.ShuoShuo.DAO.ShuoShuoStarMapper;
import com.yiban.demo.ShuoShuo.Entity.ShuoShuo;
import com.yiban.demo.ShuoShuo.Entity.ShuoShuoComment;
import com.yiban.demo.ShuoShuo.Entity.TotalShuoShuo;
import com.yiban.demo.ShuoShuo.Event.ShuoShuoCommnetEvent.PublishShuoShuoCommentEvent;
import com.yiban.demo.ShuoShuo.Event.ShuoShuoEvent.CancelStarShuoShuoEvent;
import com.yiban.demo.ShuoShuo.Event.ShuoShuoEvent.PublishShuoShuoEvent;
import com.yiban.demo.ShuoShuo.Event.ShuoShuoEvent.StarShuoShuoEvent;
import com.yiban.demo.ShuoShuo.Service.ShuoShuoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Service;

import javax.annotation.PreDestroy;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

@Service
//todo 点赞持久化和反持久化有问题
public class ShuoShuoServiceImp implements ShuoShuoService {
    private ConcurrentHashMap<Integer, AtomicInteger> starMap;
    private ConcurrentHashMap<Integer, CopyOnWriteArraySet<Integer>> starUserMap;
    private ShuoShuoMapper shuoShuoMapper;
    private ShuoShuoCommentMapper shuoShuoCommentMapper;
    private ShuoShuoStarMapper shuoShuoStarMapper;

    private volatile Lock lock;
    private ApplicationContext applicationContext;
    private ThreadPoolTaskScheduler taskScheduler;

    @Autowired
    public ShuoShuoServiceImp(ShuoShuoMapper shuoShuoMapper, ShuoShuoCommentMapper shuoShuoCommentMapper, ShuoShuoStarMapper shuoShuoStarMapper, ApplicationContext applicationContext, @Qualifier("schedulerPool") ThreadPoolTaskScheduler taskScheduler) {
        this.shuoShuoMapper = shuoShuoMapper;
        this.shuoShuoCommentMapper = shuoShuoCommentMapper;
        this.shuoShuoStarMapper = shuoShuoStarMapper;
        this.taskScheduler = taskScheduler;
        starMap = new ConcurrentHashMap<>();
        lock = new ReentrantLock();
        this.applicationContext = applicationContext;
        starUserMap = new ConcurrentHashMap<>();
        this.taskScheduler.schedule(this::dataPersistence, new CronTrigger("0 10/5 * * * ?"));
    }


    @PreDestroy
    //todo 数据量过大应该考虑分批
    public void dataPersistence(){
        if (starUserMap.isEmpty() || starMap.isEmpty()){
            return;
        }
        shuoShuoMapper.batchUpdateStars(starMap);
        starUserMap.forEach(shuoShuoStarMapper::batchUInsertStar);
    }

    public boolean publishShuoShuo(ShuoShuo shuoShuo) {
        shuoShuo.setStar(0);
        shuoShuo.setTimestamp(System.currentTimeMillis());
        int insert = shuoShuoMapper.insert(shuoShuo);
        applicationContext.publishEvent(new PublishShuoShuoEvent(this, shuoShuo));
        return insert == 1;
    }


    @Override
    public void starShuoShuo(Integer shuoshuoId, Integer userId) {
        AtomicInteger atomicInteger = starMap.get(shuoshuoId);
        int star;
        if (starUserMap.get(shuoshuoId).contains(userId)){
            return;
        }
        if (atomicInteger == null) {
            try {
                lock.lock();
                    if (starMap.get(shuoshuoId) == null){
                        starMap.put(shuoshuoId, new AtomicInteger(1));
                        star = 1;
                        CopyOnWriteArraySet<Integer> users = new CopyOnWriteArraySet<>();
                        starUserMap.put(shuoshuoId, users);
                    }else {
                        star = starMap.get(shuoshuoId).incrementAndGet();
                    }

            } finally {
                lock.unlock();
            }
        }else {
            star = atomicInteger.incrementAndGet();
        }
        starUserMap.get(shuoshuoId)
                .add(userId);
        ShuoShuo shuoShuo = new ShuoShuo();
        shuoShuo.setStar(star);
        applicationContext.publishEvent(new StarShuoShuoEvent(this,shuoShuo));
    }

    @Override
    public boolean cancelStar(Integer shuoshuoId, Integer userId) {
        if (!starUserMap.get(shuoshuoId).contains(userId)){
            return false;
        }
        AtomicInteger atomicInteger = starMap.get(shuoshuoId);
        int star;
        if (atomicInteger == null) {
            return false;
        }

        star = atomicInteger.decrementAndGet();
        starUserMap.get(shuoshuoId).remove(userId);
        ShuoShuo shuoShuo = new ShuoShuo();
        shuoShuo.setStar(star);
        applicationContext.publishEvent(new CancelStarShuoShuoEvent(this,shuoShuo));
        return true;
    }

    @Override
    public boolean publishShuoShuoComment(ShuoShuoComment shuoShuoComment,Integer userId) {
        shuoShuoComment.setScTimestamp(System.currentTimeMillis());
        shuoShuoComment.setUserId(userId);
        int i = shuoShuoCommentMapper.insert(shuoShuoComment);
        applicationContext.publishEvent(new PublishShuoShuoCommentEvent(this, shuoShuoComment));
        return i == 1;
    }

    @Override
    public List<TotalShuoShuo> getTotalShuoShuo(int current, int size) {
        return shuoShuoMapper.selectNewShuoShuo(new Page<>(current, size)).getRecords();
    }

    @Override
    public List<ShuoShuo> getShuoShuo(int current, int size) {
        return shuoShuoMapper.selectPage(new Page<>(current,size), new QueryWrapper<ShuoShuo>().orderByDesc("shuo_shuo_id"))
                .getRecords();
    }
    @Override
    public List<ShuoShuo> getByTime(long start, long end, long current, long size){
        return shuoShuoMapper
                .selectPage(new Page<>(current, size), new QueryWrapper<ShuoShuo>().between("timestamp", start, end).orderByDesc("timestamp"))
                .getRecords();
    }
    @Override
    public List<ShuoShuo> getByStarCount(long current, long size){
        return shuoShuoMapper
                .selectPage(new Page<>(current, size),new QueryWrapper<ShuoShuo>().orderByDesc("star"))
                .getRecords();
    }
}
